
/****************************************************************************
*					PlanesDetection Class implementation					*
*    				Copyright (C) 2012  J.R. Ruiz-Sarmiento					*
*																			*
*    This program is free software: you can redistribute it and/or modify	*
*    it under the terms of the GNU General Public License as published by	*
*    the Free Software Foundation, either version 3 of the License, or		*
*    (at your option) any later version.									*
*																			*
*    This program is distributed in the hope that it will be useful,		*
*    but WITHOUT ANY WARRANTY; without even the implied warranty of			*
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the			*
*    GNU General Public License for more details.							*
*																			*
*    You should have received a copy of the GNU General Public License		*
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.	*
*																			*
****************************************************************************/


// Ugly solution to solve uint32_t redefinition by MRPT and Flann, solved in MSC 2010 (defined as 1600)
#if (defined(_MSC_VER) && ( _MSC_VER < 1600 ) )	
	#include <flann/algorithms/dist.h>
#endif

#include <mrpt/system.h>

#include "CPlanesDetection.h"

#include <pcl/filters/extract_indices.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/segmentation/organized_multi_plane_segmentation.h>
#include <pcl/features/integral_image_normal.h>
#include <pcl/filters/extract_indices.h>

//#include <pcl/kdtree/kdtree_flann.h>
//#include <pcl/surface/mls.h>

#include <cmath>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

using namespace SORT::ObjectRecognition;

using namespace std;

bool CPlanesDetection::DEBUGGING_PLANES_DETECTION = false;


//-----------------------------------------------------------
//					CPlanesDetection
//-----------------------------------------------------------

CPlanesDetection::CPlanesDetection(): m_minPlaneInliers(3000),
									m_minInliersGeneral(3000),
									m_minInliersInVerticalPlanes(3000),
									m_minInliersInHorizontalPlanes(3000),
									m_detectHorizontalPlanes(false),
									m_detectVerticalPlanes(false),
									m_detectGeneralPlanes(false),
									m_distThreshold(0.02),	
									m_angleThreshold(pcl::deg2rad (3.0)),
									m_maximumCurvature(0.001)
{
	// Create an empty property tree object
    using boost::property_tree::ptree;
    ptree pt;

	string filename( "SORT.ini" );

    // Load the XML file into the property tree. If reading fails
    // (cannot open file, parse error), an exception is thrown.
    read_xml(filename, pt);

	m_useBilateralFilter = pt.get<bool>("planes_detection.execute_bilateral_filter");

	if ( pt.get<bool>("planes_detection.use_default_params") )
	{
		m_minPlaneInliers	= pt.get<double>("planes_detection.min_num_of_inliers");
		m_distThreshold		= pt.get<double>("planes_detection.distance_threshold");
		m_angleThreshold	= pt.get<double>("planes_detection.angle_threshold");
		m_maximumCurvature	= pt.get<double>("planes_detection.curvature_threshold");
	}
	else
	{

		cout << endl << "Min num of inliers: ";	cin >> m_minPlaneInliers;
		cout << "Distance threshold: ";			cin >> m_distThreshold;
		cout << "Angle threshold: ";			cin >> m_angleThreshold;
		cout << "Maximum curvature: ";			cin >> m_maximumCurvature;		
	}


	if ( pt.get<bool>("planes_detection.bilateral_filter.use_default_params") )
	{
		m_bilateralFilterConf.sigmaS	= pt.get<float>("planes_detection.bilateral_filter.sigma_s");
		m_bilateralFilterConf.sigmaR	= pt.get<float>("planes_detection.bilateral_filter.sigma_r");
		m_bilateralFilterConf.earlyDivision	= pt.get<bool>("planes_detection.bilateral_filter.early_division");
	}
	else
	{
		size_t pre_early_division;
		cout << "Sigma s: ";		cin >> m_bilateralFilterConf.sigmaS;
		cout << "Sigma r: ";		cin >> m_bilateralFilterConf.sigmaR;
		cout << "Early division: "; cin >> pre_early_division;

		( pre_early_division ) ? m_bilateralFilterConf.earlyDivision = true : m_bilateralFilterConf.earlyDivision = false;
	}

}


//-----------------------------------------------------------
//					detectPlanes
//-----------------------------------------------------------

void CPlanesDetection::detectPlanes( mrpt::aligned_containers<CPlane>::vector_t &planes )
{
	planes.clear();

	std::vector<int> inliers;

	if ( m_detectVerticalPlanes )
		detectPlanes( planes, inliers, CPlane::TPlaneType::VERTICAL );

	if ( m_detectHorizontalPlanes )
		detectPlanes( planes, inliers, CPlane::TPlaneType::HORIZONTAL );

	if ( m_detectGeneralPlanes )
		detectPlanes( planes, inliers, CPlane::TPlaneType::DIAGONAL );

}


//-----------------------------------------------------------
//					detectPlanes
//-----------------------------------------------------------

void CPlanesDetection::detectPlanes( mrpt::aligned_containers<CPlane>::vector_t &planes, std::vector<int> &indices, const CPlane::TPlaneType &type )
{
	size_t N_inliers;	// Number of inliers of the fitted plane in each iteration

	do {
		// Create the segmentation object
		pcl::SACSegmentation<pcl::PointXYZ> seg;
		pcl::PointIndices::Ptr				inliers ( new pcl::PointIndices() );
		pcl::ModelCoefficients::Ptr			coeffs( new pcl::ModelCoefficients() );
		
		// Optional
		//seg.setOptimizeCoefficients (true);

		// Mandatory
		seg.setInputCloud( m_cloud );

		if ( indices.size() ) 
		{
			vector<int>							indicesInverse;

			// Create the filtering object
			pcl::ExtractIndices<pcl::PointXYZ> extract;

			// Extract the inliers
			extract.setInputCloud ( m_cloud );
			extract.setIndices( pcl::IndicesPtr( new vector<int>(indices) ) );
			extract.setNegative ( true );
			extract.filter ( indicesInverse );

			seg.setIndices( pcl::IndicesPtr( new vector<int>(indicesInverse) ) );

			cout << "Num of indices to take into accout: " << indicesInverse.size() << endl;
			
		}

		seg.setModelType ( pcl::SAC_RANSAC );
		seg.setMaxIterations ( 1000 );
		seg.setDistanceThreshold ( 0.05 );
		
		if ( type == CPlane::TPlaneType::VERTICAL )
		{
			seg.setMethodType ( pcl::SACMODEL_NORMAL_PARALLEL_PLANE );
			seg.setAxis(Eigen::Vector3f(0,1,0));
			seg.setEpsAngle ( 0.17 ); // ~10 degrees
			cout << "----------------------" << endl;
			cout << "Searching VERTICAL" << endl;
			cout << "----------------------" << endl;
		}
		else if ( type == CPlane::TPlaneType::HORIZONTAL )
		{
			seg.setMethodType ( pcl::SACMODEL_NORMAL_PARALLEL_PLANE );
			seg.setAxis(Eigen::Vector3f(0,0,1));
			seg.setEpsAngle ( 0.17 ); // ~10 degrees
			cout << "----------------------" << endl;
			cout << "Searching HORIZONTAL" << endl;
			cout << "----------------------" << endl;
		}
		else
		{	
			cout << "----------------------" << endl;
			cout << "Searching DIAGONAL" << endl;
			cout << "----------------------" << endl;
		}

		// Detect!
		seg.segment( *inliers, *coeffs );

		N_inliers = inliers->indices.size();

		//////////////////// VISUALIZATION ///////////////////////
		boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer (new pcl::visualization::PCLVisualizer ("Detection step"));

		pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloudOriginal( new pcl::PointCloud<pcl::PointXYZRGBA>() );
		pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloudPlaneDetected( new pcl::PointCloud<pcl::PointXYZRGBA>() );

		pcl::PointCloud<pcl::PointXYZ>::Ptr cloudOriginalAux( new pcl::PointCloud<pcl::PointXYZ>() );
		pcl::PointCloud<pcl::PointXYZ>::Ptr cloudPlaneDetectedAux( new pcl::PointCloud<pcl::PointXYZ>() );

		// Create the filtering object
		pcl::ExtractIndices<pcl::PointXYZ> extract;
		/////////////////// END VISUALIZATION /////////////////////

		if ( N_inliers > m_minInliersInVerticalPlanes )
		{
			//////////////////// VISUALIZATION ///////////////////////
			if ( indices.size() )
			{
				extract.setInputCloud ( m_cloud );
				extract.setIndices ( pcl::IndicesPtr( new vector<int>(indices) ) );
				extract.setNegative ( true );
				extract.filter ( *cloudOriginalAux );

				cout << "Size of original aux: " << cloudOriginalAux->size() << endl;

				pcl::copyPointCloud(*cloudOriginalAux,*cloudOriginal);
			}
			else
			{
				pcl::copyPointCloud(*m_cloud,*cloudOriginal);

				cout << "Size of original aux (m_cloud): " << m_cloud->size() << endl;
			}
			/////////////////// END VISUALIZATION /////////////////////

			pcl::PointCloud<pcl::PointXYZ>::Ptr cloudPlaneDetectedAux( new pcl::PointCloud<pcl::PointXYZ>() );

			//CPlane plane( inliers, coeffs, type );
			//cout << plane << endl;
			//planes.push_back( plane );
			indices.insert( indices.end(), inliers->indices.begin(), inliers->indices.end() );
		}

		// Extract the inliers
		pcl::ExtractIndices<pcl::PointXYZ> extract2;
		extract2.setInputCloud ( m_cloud );
		extract2.setIndices ( inliers );
		extract2.setNegative ( false );
		extract2.filter ( *cloudPlaneDetectedAux );

		cout << "Size of plane detected aux: " << cloudPlaneDetectedAux->size() << endl;

		pcl::copyPointCloud(*cloudPlaneDetectedAux,*cloudPlaneDetected);
		
		pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZRGBA> blue(cloudPlaneDetected,0,0,255);
		pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZRGBA> red(cloudOriginal,255,0,0);

		viewer->addPointCloud<pcl::PointXYZRGBA> (cloudOriginal,red,"cloud1");
		viewer->addPointCloud<pcl::PointXYZRGBA> (cloudPlaneDetected,blue,"cloud2");

		viewer->setBackgroundColor (0.5, 0.5, 0.5);
		viewer->addCoordinateSystem (1.0);
		viewer->initCameraParameters ();

		while (!viewer->wasStopped ())
		{
			viewer->spinOnce (100);
			mrpt::system::sleep(100);
		}

	}
	while( N_inliers > m_minInliersInVerticalPlanes );
}


//-----------------------------------------------------------
//					detectPlanes
//-----------------------------------------------------------

void CPlanesDetection::detectPlanes()
{

	IF_DEBUGGING	cout << "[DETECT_PLANES] Detect planes." << endl;

	// Ensure that the members vbles involved in the process are freshhhhh
	
	m_regions.			clear();		
	m_modelCoefficients.clear();
	m_inlierIndices.	clear();
	m_labelIndices.		clear();
	m_boundaryIndices.	clear();

	if ( m_labels.use_count() )
		m_labels.reset();
	if ( m_normalCloud.use_count() )
		m_normalCloud.reset();

	m_labels		= pcl::PointCloud<pcl::Label>::Ptr( new pcl::PointCloud<pcl::Label>);
	m_normalCloud	= pcl::PointCloud<pcl::Normal>::Ptr(new pcl::PointCloud<pcl::Normal>);

	//
	// Method flow:
	//	1. Useful vbles initialization
	//	2. Normals computation
	//	3. Planes detection
	//

	//
	//	1. Useful vbles initialization
	//

	//unsigned minInliers = minInliersF * m_cloud->size();
	unsigned minInliers = m_minPlaneInliers; // Use a static num of inliers instead of an adaptative one

	//
	//	2. Computation of the normals of the point cloud
	//

	double filter_start = pcl::getTime ();

	if ( m_useBilateralFilter )
	{
		m_bilateralFilter.setInputCloud (m_cloud);

		m_bilateralFilter.setSigmaS (m_bilateralFilterConf.sigmaS);
		m_bilateralFilter.setSigmaR (m_bilateralFilterConf.sigmaR);
		m_bilateralFilter.setEarlyDivision (m_bilateralFilterConf.earlyDivision);	
	
		m_bilateralFilter.filter (*m_cloud);
	}

	double filter_end = pcl::getTime ();
	
	IF_DEBUGGING	cout << "[DETECT_PLANES] Bilateral Filter took: " << double (filter_end - filter_start) << endl;

	IF_DEBUGGING	cout << "[DETECT_PLANES] Computing normals." << endl;

	pcl::IntegralImageNormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
	ne.setNormalEstimationMethod (ne.COVARIANCE_MATRIX);
	ne.setMaxDepthChangeFactor (0.02f);
	ne.setNormalSmoothingSize (10.0f);

	double normal_start = pcl::getTime ();

	ne.setInputCloud (m_cloud);
	ne.compute (*m_normalCloud);
  
	double normal_end = pcl::getTime ();
	
	IF_DEBUGGING	cout << "[DETECT_PLANES] Normal Estimation took: " << double (normal_end - normal_start) << endl;


	// Here we go...
	//	3. Planes detection
	//
	
	IF_DEBUGGING	cout << "[DETECT_PLANES] Detecting planes." << endl;

	pcl::OrganizedMultiPlaneSegmentation<pcl::PointXYZ, pcl::Normal, pcl::Label> mps;
	mps.setMinInliers (minInliers);
	mps.setAngularThreshold ( m_angleThreshold ); 
	mps.setDistanceThreshold ( m_distThreshold ); 
	mps.setMaximumCurvature ( m_maximumCurvature );


	double plane_extract_start = pcl::getTime ();

    mps.setInputCloud (m_cloud);
	mps.setInputNormals (m_normalCloud);

    mps.segmentAndRefine (m_regions, m_modelCoefficients, m_inlierIndices, m_labels, m_labelIndices, m_boundaryIndices);
	

    double plane_extract_end = pcl::getTime ();
    
	IF_DEBUGGING	cout << "[DETECT_PLANES] Plane extraction took " << double (plane_extract_end - plane_extract_start) << endl;


	// Create a vector with the planes detected in this keyframe, and calculate their parameters (normal, center, pointclouds, etc.)
    // in the global reference
    //vector<Plane> detectedPlanes;

    /*for (size_t i = 0; i < regions.size (); i++)
    {
//    cout << "Inliers " << inlier_indices[i].indices.size() << endl;

      Plane plane;

      Eigen::Vector3f centroid = regions[i].getCentroid ();
      plane.v3center = makeVector(centroid[0],centroid[1],centroid[2]);
      plane.v3center = poseKF * plane.v3center;
      plane.v3normal[0] = model_coefficients[i].values[0]; 
	  plane.v3normal[1] = model_coefficients[i].values[1]; 
	  plane.v3normal[2] = model_coefficients[i].values[2];
      plane.v3normal = poseKF.get_rotation() * plane.v3normal;

      // Extract the planar inliers from the input cloud
      pcl::ExtractIndices<pcl::PointXYZRGBA> extract;
      extract.setInputCloud (pointCloudPtr_arg);
      extract.setIndices ( boost::make_shared<const pcl::PointIndices> (inlier_indices[i]) );
      extract.setNegative (false);
      extract.filter (*plane.planePointCloudPtr);    // Write the planar point cloud

      static pcl::VoxelGrid<pcl::PointXYZRGBA> plane_grid;
      plane_grid.setLeafSize(0.05,0.05,0.05);
      pcl::PointCloud<pcl::PointXYZRGBA> planeCloud;
      plane_grid.setInputCloud (plane.planePointCloudPtr);
      plane_grid.filter (planeCloud);
      plane.planePointCloudPtr->clear();
      pcl::transformPointCloud(planeCloud,*plane.planePointCloudPtr,pose);

      // Check whether this region correspond to the same plane as a previous one (this situation may happen when there exists a small discontinuity in the observation)
      bool isSamePlane = false;
      for (size_t j = 0; j < detectedPlanes.size(); j++)
        if( detectedPlanes[j].isSamePlane(plane, 0.017453 * 10.0, 0.1) ) // The planes are merged if they are the same
        {
          isSamePlane = true;

          *detectedPlanes[j].planePointCloudPtr += *plane.planePointCloudPtr; // Add the points of the new detection and perform a voxel grid

          static pcl::VoxelGrid<pcl::PointXYZRGBA> merge_grid;
          merge_grid.setLeafSize(0.05,0.05,0.05);
          pcl::PointCloud<pcl::PointXYZRGBA> mergeCloud;
          merge_grid.setInputCloud (detectedPlanes[j].planePointCloudPtr);
          merge_grid.filter (mergeCloud);
          detectedPlanes[j].planePointCloudPtr->clear();
          *detectedPlanes[j].planePointCloudPtr = mergeCloud;

          detectedPlanes[j].pt1 = pcl::PointXYZ (detectedPlanes[j].v3center[0], detectedPlanes[j].v3center[1], detectedPlanes[j].v3center[2]);
          detectedPlanes[j].pt2 = pcl::PointXYZ (detectedPlanes[j].v3center[0] + (0.5f * detectedPlanes[j].v3normal[0]),
                                          detectedPlanes[j].v3center[1] + (0.5f * detectedPlanes[j].v3normal[1]),
                                          detectedPlanes[j].v3center[2] + (0.5f * detectedPlanes[j].v3normal[2]));

//          cout << "\tTwo regions support the same plane in the same KeyFrame\n";
          break;
        }

      if(!isSamePlane)
      {
        plane.pt1 = pcl::PointXYZ (plane.v3center[0], plane.v3center[1], plane.v3center[2]);
        plane.pt2 = pcl::PointXYZ (plane.v3center[0] + (0.5f * plane.v3normal[0]),
                                        plane.v3center[1] + (0.5f * plane.v3normal[1]),
                                        plane.v3center[2] + (0.5f * plane.v3normal[2]));

        detectedPlanes.push_back(plane);
      }
    }*/

	IF_DEBUGGING	cout << "[DETECT_PLANES] " << m_regions.size () << " Planes detected\n";
	
}


//-----------------------------------------------------------
//				getDetectedPlanesAsPointClouds
//-----------------------------------------------------------

void CPlanesDetection::getDetectedPlanesAsPointClouds( vector<pcl::PointCloud<pcl::PointXYZ>::Ptr> &v_planes )
{
	// - Man, I need the planes inliers as independent point clouds! 
	// - Okey, here you've!

	vector<int> inliers_indices_total;	// Accumulator vble for ending up obtaining the cluttered point cloud without planes

	size_t N = m_inlierIndices.size();
	
	for ( size_t i = 0; i < N; i++ )
	{
		pcl::PointCloud<pcl::PointXYZ>::Ptr resCloud (new pcl::PointCloud<pcl::PointXYZ>);		
		pcl::IndicesPtr indices( new vector<int>( m_inlierIndices[i].indices) );
		
		// Create the filtering object
		pcl::ExtractIndices<pcl::PointXYZ> extract;

		// Extract the inliers
		extract.setInputCloud ( m_cloud );
		extract.setNegative( false );
		extract.setIndices ( indices );
		extract.filter ( *resCloud );

		// Fill the accumulator vble
		for (size_t j = 0; j < m_inlierIndices[i].indices.size(); j++)
			inliers_indices_total.push_back( m_inlierIndices[i].indices[j] );

		IF_DEBUGGING	cout << "Num of points of resCloud: " << resCloud->size() << endl;

		v_planes.push_back( resCloud );
	}

	pcl::PointCloud<pcl::PointXYZ>::Ptr resCloud (new pcl::PointCloud<pcl::PointXYZ>);
	pcl::IndicesPtr indices( new vector<int>(inliers_indices_total) );

	// Create the filtering object
	pcl::ExtractIndices<pcl::PointXYZ> extract;

	// Extract the inliers
	extract.setInputCloud ( m_cloud );
	extract.setNegative( true );
	extract.setIndices ( indices );
	extract.filter ( *resCloud );

	v_planes.push_back( resCloud );

}


//-----------------------------------------------------------
//			getDetectedPlanesAndNormalsAsPointClouds
//-----------------------------------------------------------

void CPlanesDetection::getDetectedPlanesAndNormalsAsPointClouds( vector<pcl::PointCloud<pcl::PointXYZ>::Ptr> &v_planes, 
																 vector<pcl::PointCloud<pcl::Normal>::Ptr> &v_normals  )
{
	vector<int> inliers_indices_total;

	size_t N = m_inlierIndices.size();
	

	for ( size_t i = 0; i < N; i++ )
	{
		pcl::PointCloud<pcl::PointXYZ>::Ptr resCloud (new pcl::PointCloud<pcl::PointXYZ>);		
		pcl::PointCloud<pcl::Normal>::Ptr normalCloud (new pcl::PointCloud<pcl::Normal>);

		// Create the filtering object
		pcl::ExtractIndices<pcl::PointXYZ> extract;

		pcl::IndicesPtr	indices(new vector<int> ( m_inlierIndices[i].indices) );

		// Extract the inliers
		extract.setInputCloud ( m_cloud );
		extract.setNegative( false );
		extract.setIndices ( indices );
		extract.filter ( *resCloud );

		// Create the filtering object
		pcl::ExtractIndices<pcl::Normal> extract2;
		extract2.setInputCloud ( m_normalCloud );
		extract2.setNegative( false );
		extract2.setIndices ( indices );
		extract2.filter ( *normalCloud );

		for (size_t j = 0; j < m_inlierIndices[i].indices.size(); j++)
			inliers_indices_total.push_back(m_inlierIndices[i].indices[j]);
		
		v_planes. push_back( resCloud );
		v_normals.push_back( normalCloud );

		cout << "Num of points of resCloud: " << resCloud->size() << endl;
	}

	pcl::PointCloud<pcl::PointXYZ>::Ptr resCloud (new pcl::PointCloud<pcl::PointXYZ>);		
	pcl::PointCloud<pcl::Normal>::Ptr normalCloud (new pcl::PointCloud<pcl::Normal>);
	pcl::IndicesPtr	indices(new vector<int> ( inliers_indices_total ) );

	// Create the filtering object
	pcl::ExtractIndices<pcl::PointXYZ> extract;

	// Extract the inliers
	extract.setInputCloud ( m_cloud );
	extract.setNegative( true );
	extract.setIndices ( indices );
	extract.filter ( *resCloud );

	// Create the filtering object
	pcl::ExtractIndices<pcl::Normal> extract2;
	extract2.setInputCloud ( m_normalCloud );
	extract2.setNegative( true );
	extract2.setIndices ( indices );
	extract2.filter ( *normalCloud );		

	v_planes. push_back( resCloud );
	v_normals.push_back( normalCloud );

}


//-----------------------------------------------------------
//				getDetectedPlanesAsCPlanes
//-----------------------------------------------------------

void CPlanesDetection::getDetectedPlanesAsCPlanes( mrpt::aligned_containers<CPlane>::vector_t &planes )
{
	planes.clear();

	size_t N = m_regions.size();

	planes.resize( N );

	for ( size_t i = 0; i < N; i++ )
	{
		pcl::PointIndices::Ptr	inlierIndices ( new pcl::PointIndices(m_inlierIndices[i]) );
		pcl::ModelCoefficients::Ptr	modelCoefficients ( new pcl::ModelCoefficients( m_modelCoefficients[i] ) );
		
		planes[i] = CPlane( inlierIndices, modelCoefficients, m_regions[i].getCentroid(), m_regions[i].getContour(), m_regions[i].getCovariance() );
	}
}


//-----------------------------------------------------------
//					requestConfigParams
//-----------------------------------------------------------

void CPlanesDetection::requestConfigParams()
{
	cout << endl << "Min num of inliers: ";	cin >> m_minPlaneInliers;
	cout << "Distance threshold: ";			cin >> m_distThreshold;
	cout << "Angle threshold: ";			cin >> m_angleThreshold;
	cout << "Maximum curvature: ";			cin >> m_maximumCurvature;		
	
	size_t pre_early_division;
	cout << "Sigma s: ";		cin >> m_bilateralFilterConf.sigmaS;
	cout << "Sigma r: ";		cin >> m_bilateralFilterConf.sigmaR;
	cout << "Early division: "; cin >> pre_early_division;

	( pre_early_division ) ? m_bilateralFilterConf.earlyDivision = true : m_bilateralFilterConf.earlyDivision = false;
}